using System;
using System.IO;
using System.Xml;
using System.Net;
using System.Collections.Generic;
using System.Text;
using com.flajaxian.AWS;

namespace com.flajaxian.Dime
{
    internal class DimeManager
    {
        public const int DefaultChunkSize = 0x7fffffff;

        private byte[] _envelop;
        private Stream _attachmentStream;
        private Stream _dimeStream;
        private int _chunkSize;
        private bool _isSSL;
        private string _uri;
        private readonly byte[] _limiter;
        private int _generalNumberChunks = 0;


        public DimeManager(byte[] envelop, Stream attachment) : 
            this(envelop, attachment, DimeManager.DefaultChunkSize)
        {
        }
        public DimeManager(byte[] envelop, Stream attachmentStream, int chunkSize)
        {
            this._limiter = new byte[3];
            this._envelop = envelop;
            this._attachmentStream = attachmentStream;
            this._chunkSize = chunkSize;
            this.IsSSL = true;
        }
        public int ChunkSize
        {
            get
            {
                return this._chunkSize;
            }
            set
            {
                if (value <= 0)
                {
                    throw new ArgumentException(SR.ChunkSizeWarning, SR.ChunkSize);
                }
                this._chunkSize = value;
            }
        }
        public bool IsSSL
        {
            get
            {
                return this._isSSL;
            }
            set
            {
                this._isSSL = value;
                this._uri = (this._isSSL) ? SR.UriSSL : SR.Uri;
            }
        }
        public HttpWebResponse SendRequest()
        {
            HttpWebRequest request = WebRequest.Create(this._uri) as HttpWebRequest;
            request.Method = "POST";
            request.ContentType = "application/dime";
            request.KeepAlive = true;
            request.Headers.Add("SOAPAction", @"""""");

            this.CopyDataToStream();

            request.ContentLength = this._dimeStream.Length;

            Stream requestStream = request.GetRequestStream();

            this._dimeStream.Seek(0, SeekOrigin.Begin);
            DimeHeader.CopyStream(this._dimeStream, requestStream, (int)this._dimeStream.Length);

            this._dimeStream.Close();
            requestStream.Close();

            WebResponse response = request.GetResponse();

            return ((HttpWebResponse)response);
        }

        private void CopyDataToStream()
        {
            this._dimeStream = new MemoryStream();
            List<DimeHeader> xmlHeaders = new List<DimeHeader>();
            this.GenerateHeaders(xmlHeaders, this._envelop.Length, DimeHeaderType.Xml);
            List<DimeHeader> attachmentHeaders = new List<DimeHeader>();
            this.GenerateHeaders(attachmentHeaders, this._attachmentStream.Length, DimeHeaderType.Attachment);

            foreach (DimeHeader header in xmlHeaders)
            {
                this.PopulateDestinationBySourceAndHeader(null, this._envelop, header);
            }
            foreach (DimeHeader header in attachmentHeaders)
            {
                this.PopulateDestinationBySourceAndHeader(this._attachmentStream, null, header);
            }

            //this.CreateLogFile(WriteStreamToString(this._dimeStream));
        }

        private void GenerateHeaders(List<DimeHeader> headersList, long length, DimeHeaderType type)
        {
            if (length == 0)
            {
                throw new ApplicationException(String.Format(SR.StreamHasNoContent, type));
            }
            int numberChunks = (int)Math.Ceiling(length / (decimal)this._chunkSize);
            if (type == DimeHeaderType.Xml) numberChunks = 1;
            int chunkSize = (numberChunks == 1) ? (int)length : this._chunkSize;
            Guid id = Guid.NewGuid();
            for (int i = 0; i < numberChunks; i++)
            {
                bool isFirst = (this._generalNumberChunks == 0);
                bool isLast = (type == DimeHeaderType.Attachment && i == (numberChunks - 1));
                bool isChunked = (numberChunks > 1);
                long contentLength = (numberChunks == (i + 1) && i > 0) ? length % chunkSize : chunkSize;

                DimeHeader header = new DimeHeader(id, i, type, isFirst, isLast, isChunked, contentLength);
                headersList.Add(header);

                this._generalNumberChunks++;
            }
        }

        private void PopulateDestinationBySourceAndHeader(Stream sourceStream, byte[] bytes, DimeHeader header)
        {

            header.WriteToStream(this._dimeStream);
            header.CopySourceStream(sourceStream, bytes, this._dimeStream);
        }
    }
}
